package db

import (
	"app/pkg/log"
	"fmt"
	"time"

	"github.com/garyburd/redigo/redis"
)

const (
	AccountTempCode               = "ACCOUNT_TEMP_CODE"
	resetPwdTempCode              = "RESET_PWD_TEMP_CODE"
	userIncrSeq                   = "REDIS_USER_INCR_SEQ:" // user incr seq
	appleDeviceToken              = "DEVICE_TOKEN"
	userMinSeq                    = "REDIS_USER_MIN_SEQ:"
	uidPidToken                   = "UID_PID_TOKEN_STATUS:"
	conversationReceiveMessageOpt = "CON_RECV_MSG_OPT:"
	LockProductPresuffix          = "LOCK_PRODUCT_"
)

func (d *DataBases) Exec(cmd string, key interface{}, args ...interface{}) (interface{}, error) {
	con := d.RedisPool.Get()
	if err := con.Err(); err != nil {
		log.Error("", "", "redis cmd = %v, err = %v", cmd, err)
		return nil, err
	}
	defer con.Close()

	params := make([]interface{}, 0)
	params = append(params, key)

	if len(args) > 0 {
		params = append(params, args...)
	}

	return con.Do(cmd, params...)
}

// 分布式锁
func (d *DataBases) Lock(key string) error {
	ok, err := redis.Bool(d.Exec("SETNX", key, 1))
	if err != nil {
		return err
	}
	if ok {
		//设置锁的过期时间,防止死锁
		if ok, err := redis.Bool(d.Exec("EXPIRE", key, 5)); !ok {
			d.Exec("DEL", key)
			return err
			// return fmt.Errorf("发生错误,请稍后再试")
		}
		return nil
	}
	return fmt.Errorf("发生错误,请稍后再试")
}

// 允许尝试延迟获取锁
// delay 延迟时间秒
func (d *DataBases) LockWithDelay(key string, delay int) error {
	err := d.Lock(key)
	if err != nil {
		count := 0
		ticker := time.NewTicker(time.Millisecond * 200)
		for range ticker.C {
			err = d.Lock(key)
			if err == nil {
				break
			} else {
				fmt.Println(err)
				if count > (delay * 5) {
					ticker.Stop()
					return fmt.Errorf("locking failed")
				}
				count++
			}
		}
		ticker.Stop()
	}
	return nil
}

func (d *DataBases) Unlock(key string) error {
	_, err := redis.Int(d.Exec("DEL", key))
	if err != nil {
		return err
	}
	return nil
}

func (d *DataBases) JudgeAccountEXISTS(account string) (bool, error) {
	key := AccountTempCode + account
	return redis.Bool(d.Exec("EXISTS", key))
}
func (d *DataBases) SetAccountCode(account string, code, ttl int) (err error) {
	key := AccountTempCode + account
	_, err = d.Exec("SET", key, code, "ex", ttl)
	return err
}
func (d *DataBases) GetAccountCode(account string) (string, error) {
	key := AccountTempCode + account
	return redis.String(d.Exec("GET", key))
}

func (d *DataBases) DelAccountCode(account string) (bool, error) {
	key := AccountTempCode + account
	return redis.Bool(d.Exec("DEL", key))
}
